declare type VNodeChildren = Array<?VNode | string | VNodeChildren> | string;

declare type VNodeComponentOptions = {
  Ctor: Class<Component>;
  //存放着从父组件传递到子组件的props属性，比如<one :hello="h"></one>，h="world"
  //则propsData为{h:'world'}
  //该属性在创建组件的虚拟dom对象时就解析获得了
  propsData: ?Object;
  listeners: ?Object; //组件特有的监听，需要通过$emit触发的事件
  //对于组件来说，组件内部的元素会存入该数组中，而不是VNodeWithData中的children中，
  //该数组用于解析插槽
  children: ?Array<VNode>;
  tag?: string;
};

declare type MountedComponentVNode = {
  //组件虚拟dom渲染的上下文，也就是当前虚拟dom所从属的Vue实例，
  //示例：'<div><one></one></div>'，会生成如下的虚拟dom（形象的表示），
  //'div':{children:['one']}，则该虚拟dom会创建一个Vue实例，其中one虚拟dom的context就是
  //该Vue实例。而componentInstance属性是指'one'虚拟dom所创建的Vue实例
  context: Component; //该属性是在创建虚拟dom对象时设置的
  componentOptions: VNodeComponentOptions;
  componentInstance: Component; //组件虚拟dom创建的Vue实例
  parent: VNode;
  data: VNodeData;
};

// interface for vnodes in update modules
declare type VNodeWithData = {
  tag: string;
  data: VNodeData;
  children: ?Array<VNode>;
  text: void;
  elm: any; //根据当前虚拟dom对象创建的元素节点
  ns: string | void;
  context: Component;
  key: string | number | void;
  parent?: VNodeWithData;
  //根据子虚拟dom创建Vue实例的选项对象，该属性是在创建Vnode对象时设置的
  componentOptions?: VNodeComponentOptions;
  componentInstance?: Component;
  isRootInsert: boolean;
};

declare interface VNodeData {
  //key属性的值
  key?: string | number;
  //slot属性的值
  slot?: string;
  //还存在一个refInFor属性，该属性的含义可参考compiler.js中的注释
  ref?: string;
  is?: string;
  //使用v-pre指令
  pre?: boolean;
  //is属性的值
  tag?: string;
  staticClass?: string; //直接通过class属性设置的类名，而不是通过:class动态绑定的。
  class?: any;
  //直接通过style属性设置的属性，而不是通过:style动态绑定的。
  staticStyle?: { [key: string]: any };
  style?: Array<Object> | Object;
  //保存本次diff的vnode的动态style的处理结果，这样，下一次diff时，上一次diff的vnode
  //变成了本次diff的oldVnode，但是oldVnode的动态style无需再次处理，是一种性能优化。
  //也就是说，对于同一个vnode的动态style无需多次处理。
  normalizedStyle?: Object;
  //该属性的作用是什么
  //如果是程序解析template字符串，则不会设置该属性，所有解析出的属性只存储在attrs或者domProps。
  //在transformModel方法中使用了props属性，而且开发人员直接使用渲染函数时，也可设置传递给子组件的
  //props属性的值的对象，在为子组件的props设置值时，在解析组件的props属性对应的值时，该属性的优先级比attrs高
  props?: { [key: string]: any };
  //该属性中存储着未在组件的props属性存在并且未通过.prop（使用.prop修饰时，必须使用v-bind指令，否则会作为普通attr特性）
  //修饰的attr特性
  attrs?: { [key: string]: string };
  //该属性是通过AST的props属性获得的，是dom属性（某些特性必须使用属性设置或者通过.prop修饰）
  domProps?: { [key: string]: any }; //DOM 属性
  hook?: { [key: string]: Function }; //@todo 作用是什么？ 猜测：好像是在虚拟dom的不同阶段执行不同的钩子函数，对虚拟dom进行特殊的处理。
  //由AST的events转换而来，具体可查看flow/compiler.js下关于events的注释
  on?: ?{ [key: string]: Function | Array<Function> };
  //由AST的nativeEvents转换而来，具体可查看flow/compiler.js下关于nativeEvents的注释
  nativeOn?: { [key: string]: Function | Array<Function> };
  //在动画组件中添加，该对象中存储着transition组件上设置的属性和事件，其中键是属性和事件的名，
  //值是属性和事件对应的值
  transition?: Object;
  //在动画组件transition.js中添加，该属性用于标记transition组件的子元素上存在v-show指令
  show?: boolean; // marker for v-show
  //处理inline-tempalte内联模板
  inlineTemplate?: {
    render: Function;
    staticRenderFns: Array<Function>;
  };
  //任何自定义指令和某些Vue内建指令（必须在运行时调用的内建指令）在html属性上的解析结果
  directives?: Array<VNodeDirective>;
  keepAlive?: boolean;
  //作用域插槽
  scopedSlots?: { [key: string]: Function };
  //用于处理v-model指令，
  //该属性的处理场景：
  //1）如果是组件使用该指令，则是在core/vdom/create-component.js ---> createComponent方法中处理的，
  //  通过transformModel方法处理
  model?: {
    value: any;
    callback: Function;
  };
};

//实例：v-focus:hello.go="1+1"
declare type VNodeDirective = {
  name: string; //指'focus'
  rawName: string; //指'v-focus'
  value?: any; //指'1+1'
  oldValue?: any;
  arg?: string; //指'hello'
  modifiers?: ASTModifiers; //指：{"go":true}
  //该属性保存着与当前解析结果对应的指令，比如上例中的focus对应着一个通过
  //Vue.directive方法注册或者内置在Vue构造函数的选项对象中的自定义指令。
  def?: Object;
  //还有一个expression选项，指'"1+1"'，注意，是最原始的表达式
};

declare type ScopedSlotsData = Array<{ key: string, fn: Function } | ScopedSlotsData>;
